#ifndef TCP_ENDPOINT_H
#define TCP_ENDPOINT_H

#include <unordered_map>
#include <memory>
#include <queue>

#include "base_endpoint.h"
#include "tcp_listener.h"
#include "tcp_connection.h"
#include "endpoint_addr.h"

#ifndef NATIVE_SOCKET
#include "tcp_impl.h"
#endif

namespace usnet {
class TCPEndpoint : public BaseEndpoint {
public:
    virtual ~TCPEndpoint()
    {
        conn_tab_.clear();
        tcp_listener_ = nullptr;
    }

    TCPEndpoint(const struct sockaddr_in& sockaddr):BaseEndpoint(sockaddr)
    {
        // nothing to do
    }

    TCPEndpoint(const struct sockaddr_in6& sockaddr):BaseEndpoint(sockaddr)
    {
        // nothing to do
    }

    int Connect(const BaseEndpoint* rep) override;
    
    int Accept() override;
    
    inline int Close(const int fd) override
    {
        if (conn_tab_.count(fd)) {
            conn_tab_.erase(fd);
        }
        return 0;
    }

    inline int Send(const int fd, const char* data, const uint32_t len) override
    {
        if (conn_tab_.count(fd) == 0) {
            return -1;
        }
        return conn_tab_.at(fd)->Send(data, len);
    }

    inline int Recv(const int fd, char* buf, const uint32_t len) override
    {
        if (conn_tab_.count(fd) == 0) {
            return -1;
        }
        return conn_tab_.at(fd)->Recv(buf, len);
    }
    
    inline int Send(const int fd, const struct iovec* iov, const uint32_t len) override
    {
        if (conn_tab_.count(fd) == 0) {
            return -1;
        }
        return conn_tab_.at(fd)->Send(iov, len);
    }

    inline int Recv(const int fd, struct iovec* iov, const uint32_t len) override
    {
        if (conn_tab_.count(fd) == 0) {
            return -1;
        }
        return conn_tab_.at(fd)->Recv(iov, len);
    }

#ifndef NATIVE_SOCKET
    inline uint32_t Recv(const int fd, const struct iovec** iov, const uint32_t size) override
    {
        if (conn_tab_.count(fd) == 0) {
            return 0;
        }
        return conn_tab_.at(fd)->Recv(iov, size);
    }

    inline uint32_t Reserve(const int fd, const struct iovec** iov, const uint32_t size) override
    {
        if (conn_tab_.count(fd) == 0) {
            return 0;
        }
        return conn_tab_.at(fd)->Reserve(iov, size);
    }

    inline void Discard(const int fd, const struct iovec* iov) override
    {
        if (conn_tab_.count(fd)) {
            return conn_tab_.at(fd)->Discard(iov);
        }
    }

    inline bool Commit(const int fd, const struct iovec* iov) override
    {
        if (conn_tab_.count(fd) == 0) {
            return false;
        }
        return conn_tab_.at(fd)->Commit(iov);
    }
#endif

private:

    std::unique_ptr<TCPListener> tcp_listener_ { nullptr };
    std::unordered_map<int, std::unique_ptr<TCPConnection>> conn_tab_ { };
};
} // end of namespace usnet
#endif